package com.whl.basic.interceptor;


import com.whl.basic.annotation.PreAuthorize;
import com.whl.basic.jwt.JwtUtils;
import com.whl.basic.jwt.LoginData;
import com.whl.basic.jwt.Payload;
import com.whl.basic.jwt.RsaUtils;
import com.whl.user.domain.Logininfo;
import com.whl.user.mapper.LogininfoMapper;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.security.PublicKey;
import java.util.List;

/**
 * 登陆拦截
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.获取token
        String token = request.getHeader("token");
        // 3.如果有token,通过token获取登录信息
        if (token != null) {
            // 获取公钥
            PublicKey publicKey = RsaUtils.getPublicKey(RsaUtils.class.getClassLoader().getResource("auth_rsa.pub").getFile());
            try {
                // 根据公钥解析jwtToken串 - loginData
                Payload<LoginData> payload = JwtUtils.getInfoFromToken(token, publicKey, LoginData.class);
                // Object obj = redisTemplate.opsForValue().get(token); // logininfo对象
                if (payload != null) { // 登陆成功，而且没有过期
                    // 已经登录成功就应该验证权限
                    // 1.如果是前端用户 - 直接放行
                    Logininfo logininfo = payload.getLoginData().getLogininfo();
                    if (logininfo.getType() == 1) {
                        return true;
                    }
                    // 2.如果是后端管理员，就应验证权限【后端管理员 - 角色不一样 - 权限不一样】
                    // 3.获取当前接口上的注解里的sn - 用handler - 获取注解@PreAuthorize - 获取注解里的sn
                    HandlerMethod handlerMethod = (HandlerMethod) handler; // 就是接受请求的接口 - 方法
                    PreAuthorize annotation = handlerMethod.getMethodAnnotation(PreAuthorize.class);
                    if (annotation == null) { // 当前接口的方法上没有这个注解 - 不需要校验 - 直接放行
                        return true;
                    } else {
                        String sn = annotation.sn();
                        // 4.根据logininfoId获取当前登录人的所有权限 - sn
                        List<String> sns = logininfoMapper.findPermissionsByLogininfoId(logininfo.getId());
                        if (sns.contains(sn)) { // 说明有权限
                            return true;
                        }
                        // 说明没有权限 - 会经过后置拦截器，可以在axios后置拦截器中判断弹出错误框
                        response.setContentType("application/json;charset= UTF-8");
                        response.getWriter().println("{\"success\":false,\"message\":\"noPermission\"}");
                        return false;
                    }
                }
            } catch(ExpiredJwtException e){
                // 说明没有权限 - 会经过后置拦截器，可以在axios后置拦截器中判断弹出错误框
                response.setContentType("application/json;charset= UTF-8");
                response.getWriter().println("{\"success\":false,\"message\":\"timeout\"}");
                return false;
            }

                // 5.如果登录信息不为null， 放行+刷新过期时间 【重新添加到redis】
                // redisTemplate.opsForValue().set(token,obj,30, TimeUnit.MINUTES);

                // 已经登录成功就应该验证权限
                // 1.如果是前端用户 - 直接放行
                // Logininfo logininfo = (Logininfo)obj;

        }
        // 4.如果登录信息为null，过期了 直接拦截 响应前端 - 跳转到登录页面
        // 2.判断token，如果为null，直接拦截 响应前端 - 跳转到登录页面
        // 告诉浏览器我要给你响应的是json数据，编码集是utf-8
        response.setContentType("application/json;charset= UTF-8");
        response.getWriter().println("{\"success\":false,\"message\":\"noLogin\"}");
        return false;
    }
}
